
package de.unihannover.se.restlab.microblog_client;

import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URL;
import java.util.LinkedList;
import java.util.List;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import com.sun.jersey.api.client.Client;
import com.sun.jersey.api.client.ClientResponse;
import com.sun.jersey.api.client.WebResource;
import com.sun.jersey.api.client.filter.HTTPBasicAuthFilter;
import com.sun.jersey.core.util.MultivaluedMapImpl;
import com.sun.xml.wss.impl.misc.URI;

import de.unihannover.se.restlab.microblog_client.models.Account;
import de.unihannover.se.restlab.microblog_client.models.Link;
import de.unihannover.se.restlab.microblog_client.models.Message;
import de.unihannover.se.restlab.microblog_client.models.User;

public class MicroClient {

	private static final String STOPWORDS_FILENAME = "stopwords.txt";

	private static Client c;

	private static User[] users;
	private static Message[] msgs;
	private static Account[] accounts;

	private static Link messagesLink;
	private static Link usersLink;
	private static Link accountsLink;

	private static String storedUsername, storedPassword;
	
	public static List<String> stopwordsEinlesen(String filename) throws Exception {
		List<String> stopwords = new LinkedList<String>();
		
		FileReader fr = new FileReader(filename);
		BufferedReader br = new BufferedReader(fr);
		
		while(true) {
			String line = br.readLine();
			if(line == null) break;
			
			if(line.startsWith(";")) continue;
			
			stopwords.add(line);
		}		
		
		return stopwords;
	}
	
	public static List<String> interestingKeywordsInMsg(Message msg) {		
		List<String> keywords = new LinkedList<String>();
		
		try {
			List<String> stopWords = stopwordsEinlesen(STOPWORDS_FILENAME);
			
			String[] words = msg.getContent().split(" ");
			for(String word : words) {
				if(stopWords.contains(word))
					continue;
				
				keywords.add(word);				
			}
			
		} catch (Exception e) {
			// TODO Auto-generated catch block
			System.out.println("Fehler beim Einlesen der Stoppwörter!");
			e.printStackTrace();
		}
		
		return keywords;
	}
	
	public static List<String> retrieveVideoLinks(Message m) throws Exception {
		List<String> keywords = interestingKeywordsInMsg(m);
		LinkedList<String> videoLinks = new LinkedList<String>();

		String requestURI = "http://gdata.youtube.com/feeds/api/videos/-/";
		for(String keyword : keywords) {
			requestURI += "%7Bhttp%3A%2F%2Fgdata.youtube.com%2Fschemas%2F2007%2Fkeywords.cat%7D"+ keyword + "/";
		}		
		requestURI += "?max-results=5";
		
		URL url = new URL(requestURI); 
		DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
		DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
		Document doc = dBuilder.parse(url.openStream());
		doc.getDocumentElement().normalize();
 
		//System.out.println("Root element :" + doc.getDocumentElement().getNodeName());
		NodeList nList = doc.getElementsByTagName("entry");
		for (int temp = 0; temp < nList.getLength(); temp++) {
			Node nNode = nList.item(temp);
			if (nNode.getNodeType() == Node.ELEMENT_NODE) {
				Element eElement = (Element) nNode;
				Node nValue = eElement.getElementsByTagName("link").item(0).getAttributes().getNamedItem("href");
				videoLinks.add(nValue.getNodeValue());
			}
		}
				
		return videoLinks;
	}
	
	private static void testRetrieveVideoLinks() throws Exception {
		Message m = new Message(0, null, "fussball macht spass");
		
		for(String keyword : interestingKeywordsInMsg(m)) System.out.println(keyword);
		
		List<String> links = retrieveVideoLinks(m);
		for(String link : links) System.out.println(link);
	}

	
	public static void main (String[] args) {
//		try {
//			testRetrieveVideoLinks();
//		} catch (Exception e1) {
//			// TODO Auto-generated catch block
//			e1.printStackTrace();
//		}
//		return;
//		
		c = Client.create();

		updateLinks();
		updateUsers();
		updateMessages();

		boolean done = false;
		String sepStr = sep('=', 40) + "\n";

		while (!done) {
			l(sepStr + "Optionen\n" + sepStr + "1. Nutzerliste anzeigen\n" + "2. Nachrichtenliste anzeigen\n"
				+ "3. Nutzer anlegen\n" + "4. Nachricht schreiben\n" + "5. Accountliste anzeigen\n" + "6. Beenden");

			try {
				int sel = Integer.valueOf(prompt("Auswahl"));
				switch (sel) {
				case 1:
					showUserList();
					break;
				case 2:
					String filterQuestion = prompt("Nach bestimmtem Nutzer filtern? (y/n)");
					showMessageList(filterQuestion.startsWith("y") ? getId(prompt("Name des Nutzers")) : -1);
					break;
				case 3:
					addUser();
					break;
				case 4:
					addMessage();
					break;
				case 5:
					showAccountList();
					break;
				case 6:
					done = true;
					break;
				default:
				}
				pause();
			} catch (Exception e) {
				System.out.println("Fehler: bitte neue Eingaben tätigen.");
				e.printStackTrace();
			}
		}
	}

	private static void pause () throws Exception {
		System.out.println("Press return to continue...");
		new BufferedReader(new InputStreamReader(System.in)).readLine();
	}

	private static String sep (char c, int times) {
		StringBuffer sbuf = new StringBuffer();
		for (int i = 0; i < times; i++)
			sbuf.append(c);
		return sbuf.toString();
	}

	private static void addMessage () throws Exception {
		MultivaluedMapImpl formData = new MultivaluedMapImpl();

		String username = prompt("Name des Autors");
		formData.add("userId", String.valueOf(getId(username)));
		String msgContent = prompt("Inhalt der Nachricht");
		formData.add("content", msgContent);
		formData.add("parentId", String.valueOf(-1));

		if (post(messagesLink.getHref(), formData) == null) return;

		updateMessages();
		l("Neue Nachricht geadded.");
	}

	private static void addUser () throws Exception {
		MultivaluedMapImpl formData = new MultivaluedMapImpl();
		String username = prompt("Name des neuen Nutzers");
		String password = prompt("Passwort des neuen Nutzers");
		formData.add("name", username);
		formData.add("password", password);

		if (post(usersLink.getHref(), formData) == null) return;

		updateUsers();
		l("Neuer Nutzer geadded. Seine id ist: " + getId(username));
	}

	private static void showMessageList (int userId) throws Exception {
		for (Message m : msgs) {
			// TODO: Videolinks nur in Ausgabe (View) oder auch in Modell?
			String linkstr = "";
			
			List<String> links = retrieveVideoLinks(m);
			int i=1;
			for(String link : links) {
				linkstr += "Link Nr. " + (i++) + ": " + link.replace("&feature=youtube_gdata", "") + "\n";
			}
			
			String messageStr = m.toString() + "\n" + linkstr + "\n";
			if (userId == -1 || m.getUser().getId() == userId) System.out.print(messageStr);
		}

		String choice = prompt("Eine Nachricht rebloggen? (y/n)");
		if (choice.startsWith("y") || choice.startsWith("Y")) {
			int parentId = Integer.valueOf(prompt("ID der zu rebloggenden Nachricht"));

			Message msg = null;

			for (int i = 0; i < msgs.length; i++)
				if (msgs[i].getId() == parentId) msg = msgs[i];

			String reblogLink = msg.getSelf().getHref() + "/reblog";
			String username = prompt("Name des Autors");

			MultivaluedMapImpl formData = new MultivaluedMapImpl();
			formData.add("userId", String.valueOf(getId(username)));

			if (post(reblogLink, formData) == null) return;

			updateMessages();
			l("Neuer Reblog geadded.");
		}
	}

	private static void showAccountList () {
		if (accounts == null) updateAccounts();

		if (!(accounts == null)) {
			for (Account a : accounts) {
				System.out.println(a);
			}
		}
	}

	private static void showUserList () {
		for (User u : users)
			System.out.println(u);
	}

	private static void updateLinks () {
		ClientResponse cresp = acpt("http://localhost:8080/microblog");
		Link[] links = cresp.getEntity(Link[].class);

		usersLink = messagesLink = accountsLink = null;

		// Microblog-Objekt auslesen scheiterte -> Link-Array (Danke Gerrit!)
		String rel;
		for (Link l : links) {
			rel = l.getRel();
			usersLink = rel.equals("users") ? l : usersLink;
			messagesLink = rel.equals("messages") ? l : messagesLink;
			accountsLink = rel.equals("accounts") ? l : accountsLink;
			l("Ressourcenlink: " + l);
		}
	}

	private static void updateAccounts () {
		ClientResponse cresp = acpt(accountsLink.getHref());
		if (cresp == null) {
			l("Nicht autorisiert Accounts einzulesen!");
			return;
		}
		accounts = cresp.getEntity(Account[].class);
		l(accounts.length + " Accounts eingelesen...");
	}

	private static void updateMessages () {
		msgs = acpt(messagesLink.getHref()).getEntity(Message[].class);
		l(msgs.length + " Nachrichten eingelesen...");
	}

	private static void updateUsers () {
		users = acpt(usersLink.getHref()).getEntity(User[].class);
		l(users.length + " Nutzer eingelesen...");
	}

	private static int getId (String name) throws Exception {
		for (User u : users) {
			if (u.getName().equals(name)) return u.getId();
		}

		throw new Exception("Ungueltiger Nutzername!");
	}

	private static String prompt (String msg) throws IOException {
		System.out.print(msg + ": ");
		InputStreamReader isr = new InputStreamReader(System.in);
		BufferedReader br = new BufferedReader(isr);
		return br.readLine();
	}

	private static void l (String s) {
		System.out.println(s);
	}

	private static ClientResponse acpt (String resUri) {
		return unified(resUri, null);
	}

	private static ClientResponse post (String href, MultivaluedMapImpl formData) {
		return unified(href, formData);
	}

	private static ClientResponse unified (String href, MultivaluedMapImpl formData) {
		// first try without basic auth
		ClientResponse cresp = (formData == null) ? c.resource(href).accept("application/xml").get(ClientResponse.class) :
			c.resource(href).type("application/x-www-form-urlencoded").post(ClientResponse.class, formData);

		int status = cresp.getStatus();
		l("Status: " + status + ((status == 401) ? "-> nicht autorisiert!" : ""));
		
		// cresp.getHeaders() auf Basic durchsuchen
		List<String> strs = (cresp.getHeaders().get("www-Authenticate"));
		if(strs != null) {
			if(strs.get(0).startsWith("Basic"))
				System.out.println("Basic auth wird verwendet!");
		}
			
		
		if(status != 200)
			l("Fehler!");

		// haven't tried stored user/pass-pair yet
		boolean triedStored = false;

		while (status == 401) {
			l("Not authorized!");
			try {
				// prompt for user/pass-pair if none stored OR stored already tried
				if (storedUsername == null || storedUsername.length() < 1 || triedStored) {
					storedUsername = prompt("Please enter username (or empty string)");
					if (storedUsername.length() == 0) return null;
					storedPassword = prompt("Please enter password (or empty string)");
					if (storedPassword.length() == 0) return null;
				} else { // haven't prompted -> trying stored
					triedStored = true;
				}
				
				WebResource res = c.resource(href);
				res.addFilter(new HTTPBasicAuthFilter(storedUsername, storedPassword));

				cresp = (formData == null) ? res.accept("application/xml").get(ClientResponse.class) : res.type("application/x-www-form-urlencoded").post(ClientResponse.class, formData);

				status = cresp.getStatus();
				
				if(status == 200)
					break;
				
			} catch (Exception e) {
				l("Input error!");
				e.printStackTrace();
			}

		}

		return cresp;
	}

}
